#pragma once 

#include <iostream>
#include <fstream>
#include <pthread.h>
#include <vector>
#include <sstream>
#include <unordered_map>
#include "Sockets.hpp"

using namespace std;

class HttpServer;

struct ThreadDate
{
public:
    ThreadDate(int sockfd, HttpServer* hs)
        : _sockfd(sockfd)
        , _sev(hs)
    {}

public:
    int _sockfd;
    HttpServer* _sev;
};

const uint16_t defaultport = 8080;
const string webroot = "./wwwroot";
const string sep = "\r\n";
const std::string homepage = "index.html";

class HttpRequest
{
public:
    void Deserialize(string content)
    {
        while (true)
        {
            size_t pos = content.find(sep);
            if(pos == string::npos)
                break;
            req_head.push_back(content.substr(0, pos));
            content.erase(0, pos + sep.size());
        }     
        text = content;
    }

    void Prase()
    {
        stringstream ss(req_head[0]);
        ss >> method >> url >> http_version;
        file_path = webroot;
        if(url == "/" || url == "/index.html")
        {
            file_path += "/";
            file_path += homepage;
        }
        else
        {
            file_path += url;
        }

        auto pos = file_path.rfind(".");
        if(pos == string::npos) 
            suffix = ".html";
        else 
            suffix = file_path.substr(pos);
    }

    void DebugPrint()
    {
        for(auto &line : req_head)
        {
            std::cout << "--------------------------------" << std::endl;
            std::cout << line << "\n\n";
        }

        std::cout << "method: " << method << std::endl;
        std::cout << "url: " << url << std::endl;
        std::cout << "http_version: " << http_version << std::endl;
        std::cout << "file_path: " << file_path << std::endl;
        std::cout << text << std::endl;
    }

public:
    vector<string> req_head;
    string text;

    string method;
    string url;
    string http_version;
    string file_path;
    string suffix;
};

class HttpServer
{
public:
    HttpServer(uint16_t port = defaultport)
        : _port(port)
    {
        _content_type.insert({".html", "text/html"});
        _content_type.insert({".png", "image/png"});
    }

    ~HttpServer()
    {}

public:
    void Init()
    {
        _listensock.Socket();
        _listensock.Bind(_port);
        _listensock.Listen();
    }

    bool Start()
    {   
        for(;;)
        {
            string clientip;
            uint16_t clientport;
            int sockfd = _listensock.Accept(&clientip, &clientport);

            pthread_t tid;
            ThreadDate* td = new ThreadDate(sockfd, this);
            pthread_create(&tid, nullptr, ThreadRun, td);

        }
    }

    static string ReadHtmlPath(const string& filepath)
    {
        ifstream in(filepath, ios::binary);
        if(!in.is_open()) 
            return "";

        in.seekg(0, ios_base::end);
        auto len = in.tellg();
        in.seekg(0, ios_base::beg);

        string content;
        content.resize(len);

        in.read((char*)content.c_str(), content.size());
        //std::string content;
        //std::string line;
        //while(std::getline(in, line))
        //{
        //    content += line;
        //}

        in.close();
        return content;
    }

    string SuffixToDesc(const std::string &suffix)
    {
        auto iter = _content_type.find(suffix);
        if(iter == _content_type.end()) 
            return _content_type[".html"];
        else 
            return _content_type[suffix];
    }

    void HandlerHttp(int sockfd)
    {
        char buffer[16384];
        ssize_t n = recv(sockfd, buffer, sizeof(buffer) - 1, 0);
        if(n > 0)
        {
            buffer[n] = 0;
            cout << buffer;
            HttpRequest req;
            req.Deserialize(buffer);
            req.Prase();

            // 返回响应的过程
            string text;
            bool ok = true;
            text = ReadHtmlPath(req.file_path); // 失败？
            if(text.empty())
            {
                ok = false;
                string err_html = webroot;
                err_html += "/";
                err_html += "err.html";
                text = ReadHtmlPath(err_html);
            }

            string response_line;
            if(ok)
                response_line = "HTTP/1.0 200 OK\r\n";
            else
                response_line = "HTTP/1.0 404 Not Found\r\n";

            std::string response_header = "Content-Length: ";
            response_header += std::to_string(text.size()); 
            response_header += "\r\n";
            response_header += "Content-Type: ";
            response_header += SuffixToDesc(req.suffix);
            response_header += "\r\n";
            response_header += "Set-Cookie: name=haha&&passwd=12345";
            response_header += "\r\n";

            string space_line = "\r\n";
            
            string response = response_line;
            response += response_header;
            response += space_line;
            response += text;

            send(sockfd, response.c_str(), response.size(), 0);
        }
        close(sockfd);
    }

    static void* ThreadRun(void* args)
    {
        pthread_detach(pthread_self());
        ThreadDate* td = static_cast<ThreadDate*>(args);

        td->_sev->HandlerHttp(td->_sockfd);
        delete td;
        return nullptr;
    }   

private:
    Sockets _listensock;
    uint16_t _port;
    unordered_map<std::string, std::string> _content_type;
};